home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / imap.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  30KB  |  1,142 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: imap.c,v 4.79 1996/03/15 07:13:42 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    USENET News reading additions in part by L Lundblade / NorthWestNet, 1993
  32.    lgl@nwnet.net
  33.  
  34.    Pine is in part based on The Elm Mail System:
  35.     ***********************************************************************
  36.     *  The Elm Mail System  -  Revision: 2.13                             *
  37.     *                                                                     *
  38.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  39.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  40.     ***********************************************************************
  41.  
  42.  
  43.   ----------------------------------------------------------------------*/
  44.  
  45. /*======================================================================
  46.     imap.c
  47.     The call back routines for the c-client/imap
  48.        - handles error messages and other notification
  49.        - handles prelimirary notification of new mail and expunged mail
  50.        - prompting for imap server login and password 
  51.  
  52.  ====*/
  53.  
  54. #include "headers.h"
  55.  
  56.  
  57. /*
  58.  * struct used to keep track of password/host/user triples.
  59.  * The problem is we only want to try user names and passwords if
  60.  * we've already tried talking to this host before.
  61.  * 
  62.  */
  63. typedef struct mmlogin_s {
  64.     char       *user,
  65.            *host,
  66.            *passwd;
  67.     struct mmlogin_s *next;
  68. } MMLOGIN_S;
  69.  
  70.  
  71. /*
  72.  * Internal prototypes
  73.  */
  74. int   imap_get_passwd PROTO((MMLOGIN_S *, char *, char *, char *));
  75. void  imap_set_passwd PROTO((MMLOGIN_S **, char *, char *, char *));
  76. #if defined(DOS) || defined(OS2)
  77. char  xlate_in PROTO((int));
  78. char  xlate_out PROTO((char));
  79. char *passfile_name PROTO((char *, char *));
  80. int   read_passfile PROTO((char *, MMLOGIN_S **));
  81. void  write_passfile PROTO((char *, MMLOGIN_S *));
  82. int   get_passfile_passwd PROTO((char *, char *, char *, char *));
  83. void  set_passfile_passwd PROTO((char *, char *, char *, char *));
  84. #endif
  85.  
  86.  
  87. /*
  88.  * Exported globals setup by searching functions to tell mm_searched
  89.  * where to put message numbers that matched the search criteria,
  90.  * and to allow mm_searched to return number of matches.
  91.  */
  92. MAILSTREAM *mm_search_stream;
  93. long        mm_search_count    = 0L;
  94.  
  95.  
  96. /*
  97.  * Exported globals used to report folder FIND/LIST responses.
  98.  */
  99. void       *find_folder_list   = NULL;
  100. MAILSTREAM *find_folder_stream = NULL;
  101. long        find_folder_count  = 0L;
  102. int        find_folder_inbox    = 0;
  103. #ifdef NEWBB
  104. void       *newbb_folder_list;
  105. #endif
  106.  
  107.  
  108. /*
  109.  * Local global to hook cached list of host/user/passwd triples to.
  110.  */
  111. static    MMLOGIN_S    *mm_login_list = NULL;
  112.  
  113.  
  114.  
  115. /*----------------------------------------------------------------------
  116.       Write imap debugging information into log file
  117.  
  118.    Args: strings -- the string for the debug file
  119.  
  120.  Result: message written to the debug log file
  121.   ----*/
  122. void
  123. mm_dlog(string)
  124.     char *string;
  125. {
  126. #ifdef    DEBUG
  127.     time_t      now;
  128.     struct tm  *tm_now;
  129.  
  130.     now = time((time_t *)0);
  131.     tm_now = localtime(&now);
  132.     dprint(0, (debugfile, "IMAP DEBUG %2.2d:%2.2d:%2.2d %d/%d: %s\n",
  133.            tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
  134.            tm_now->tm_mon+1, tm_now->tm_mday, string));
  135. #endif
  136. }
  137.  
  138.  
  139.  
  140. /*----------------------------------------------------------------------
  141.       Queue imap log message for display in the message line
  142.  
  143.    Args: string -- The message 
  144.          errflg -- flag set to 1 if pertains to an error
  145.  
  146.  Result: Message queued for display
  147.  
  148.  The c-client/imap reports most of it's status and errors here
  149.   ---*/
  150. void
  151. mm_log(string, errflg)
  152.     char *string;
  153.     long  errflg;
  154. {
  155.     char        message[300];
  156.     char       *occurance;
  157.     int         was_capitalized;
  158.     time_t      now;
  159.     struct tm  *tm_now;
  160.  
  161.     now = time((time_t *)0);
  162.     tm_now = localtime(&now);
  163.  
  164.     dprint((errflg == ERROR ? 1 : 2),
  165.        (debugfile,
  166.         "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
  167.         tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec, tm_now->tm_mon+1,
  168.         tm_now->tm_mday,
  169.         (errflg == ERROR)
  170.           ? "ERROR"
  171.           : (errflg == WARN)
  172.           ? "warn"
  173.           : (errflg == PARSE)
  174.               ? "parse"
  175.               : "babble",
  176.         string));
  177.  
  178.     if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
  179.     ps_global->try_to_create = 1;
  180.     return;
  181.     }
  182.     else if(ps_global->try_to_create
  183.        || (ps_global->dead_stream
  184.        && (!strncmp(string, "[CLOSED]", 8) || strstr(string, "No-op"))))
  185.       /*
  186.        * Don't display if creating new folder OR
  187.        * warning about a dead stream ...
  188.        */
  189.       return;
  190.  
  191.     /*---- replace all "mailbox" with "folder" ------*/
  192.     strncpy(message, string, sizeof(message));
  193.     message[sizeof(message) - 1] = '\0';
  194.     occurance = srchstr(message, "mailbox");
  195.     while(occurance) {
  196.     if(!*(occurance+7) || isspace(*(occurance+8))){
  197.         was_capitalized = isupper(*occurance);
  198.         rplstr(occurance, 7, (errflg == PARSE ? "address" : "folder"));
  199.         if(was_capitalized)
  200.           *occurance = (errflg == PARSE ? 'A' : 'F');
  201.     }
  202.     else
  203.       occurance += 7;
  204.  
  205.         occurance = srchstr(occurance, "mailbox");
  206.     }
  207.  
  208.     if(errflg == PARSE || ps_global->noshow_error) 
  209.       strcpy(ps_global->c_client_error, message);
  210.  
  211.     if(ps_global->noshow_error ||
  212.                (ps_global->noshow_warn && errflg == WARN) ||
  213.                        (errflg != ERROR && errflg != WARN))
  214.       return; /* Only care about errors; don't print when asked not to */
  215.  
  216.     if(errflg == ERROR)
  217.       ps_global->mm_log_error = 1;
  218.  
  219.     /*---- Display the message ------*/
  220.     q_status_message(SM_ORDER | SM_DING, 3, 5, message);
  221.     strcpy(ps_global->last_error, message);
  222. }
  223.  
  224.  
  225.  
  226. /*----------------------------------------------------------------------
  227.          recieve notification from IMAP
  228.  
  229.   Args: stream  --  Mail stream message is relavant to 
  230.         string  --  The message text
  231.         errflag --  Set if it is a serious error
  232.  
  233.  Result: message displayed in status line
  234.  
  235.  The facility is for general notices, such as connection to server;
  236.  server shutting down etc... It is used infrequently.
  237.   ----------------------------------------------------------------------*/
  238. void
  239. mm_notify(stream, string, errflag)
  240.     MAILSTREAM *stream;
  241.     char       *string;
  242.     long        errflag;
  243. {
  244.     /* be sure to log the message... */
  245.     dprint(1, (debugfile, "IMAP mm_notify %s : %s : %s\n",
  246.                (!errflag) ? "NIL" : 
  247.          (errflag == ERROR) ? "error" :
  248.            (errflag == WARN) ? "warning" :
  249.              (errflag == BYE) ? "bye" : "unknown",
  250.            (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
  251.            string));
  252.  
  253.     sprintf(ps_global->last_error, "%.50s : %.*s",
  254.         (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
  255.         min(MAX_SCREEN_COLS, 500-70),
  256.         string);
  257.     ps_global->last_error[ps_global->ttyo->screen_cols] = '\0';
  258.  
  259.     /*
  260.      * Then either set special bits in the pine struct or
  261.      * display the message if it's tagged as an "ALERT" or
  262.      * its errflag > NIL (i.e., WARN, or ERROR)
  263.      */
  264.     if(errflag == BYE){
  265.     if(stream == ps_global->mail_stream){
  266.         if(ps_global->dead_stream)
  267.           return;
  268.         else
  269.           ps_global->dead_stream = 1;
  270.     }
  271.     else if(stream && stream == ps_global->inbox_stream){
  272.         if(ps_global->dead_inbox)
  273.           return;
  274.         else
  275.           ps_global->dead_inbox = 1;
  276.     }
  277.     }
  278.     else if(!strncmp(string, "[TRYCREATE]", 11))
  279.       ps_global->try_to_create = 1;
  280.     else if(!strncmp(string, "[ALERT]", 7))
  281.       q_status_message2(SM_MODAL, 3, 3, "%s : %s",
  282.             (stream && stream->mailbox)
  283.               ? stream->mailbox : "-no folder-",
  284.             string + 7);
  285.     else if(!strncmp(string, "[READ-ONLY]", 11))
  286.       q_status_message2(SM_ORDER | SM_DING, 3, 3, "%s : %s",
  287.             (stream && stream->mailbox)
  288.               ? stream->mailbox : "-no folder-",
  289.             string + 11);
  290.     else if(errflag && errflag != BYE)
  291.       q_status_message(SM_ORDER | ((errflag == ERROR) ? SM_DING : 0),
  292.                3, 6, ps_global->last_error);
  293. }
  294.  
  295.  
  296.  
  297. /*----------------------------------------------------------------------
  298.        receive notification of new mail from imap daemon
  299.  
  300.    Args: stream -- The stream the message count report is for.
  301.          number -- The number of messages now in folder.
  302.  
  303.   Result: Sets value in pine state indicating new mailbox size
  304.  
  305.      Called when the number of messages in the mailbox goes up.  This
  306.  may also be called as a result of an expunge. It increments the
  307.  new_mail_count based on a the difference between the current idea of
  308.  the maximum number of messages and what mm_exists claims. The new mail
  309.  notification is done in newmail.c
  310.  
  311.  Only worry about the cases when the number grows, as mm_expunged
  312.  handles shrinkage...
  313.  
  314.  ----*/
  315. void
  316. mm_exists(stream, number)
  317.     MAILSTREAM *stream;
  318.     long number;
  319. {
  320.     long new_this_call;
  321.  
  322.     dprint(3, (debugfile, "=== mm_exists(%ld,%s) called ===\n", number,
  323.      !stream ? "(no stream)" : !stream->mailbox ? "(null)" : stream->mailbox));
  324.  
  325.     if(stream == ps_global->mail_stream){
  326.     if(mn_get_total(ps_global->msgmap) != number){
  327.         ps_global->mail_box_changed = 1;
  328.         ps_global->mangled_header   = 1;
  329.     }
  330.  
  331.         if(mn_get_total(ps_global->msgmap) < number){
  332.         new_this_call = number - mn_get_total(ps_global->msgmap);
  333.         ps_global->new_mail_count += new_this_call;
  334.         if(mn_get_sort(ps_global->msgmap) != SortArrival 
  335.            || mn_get_revsort(ps_global->msgmap))
  336.           clear_index_cache();
  337.  
  338.         mn_add_raw(ps_global->msgmap, new_this_call);
  339.     }
  340.     } else if(stream == ps_global->inbox_stream) {
  341.     if(mn_get_total(ps_global->inbox_msgmap) != number)
  342.       ps_global->inbox_changed = 1;
  343.  
  344.         if(mn_get_total(ps_global->inbox_msgmap) < number){
  345.         new_this_call = number - mn_get_total(ps_global->inbox_msgmap);
  346.         ps_global->inbox_new_mail_count += new_this_call;
  347.         mn_add_raw(ps_global->inbox_msgmap, new_this_call);
  348.     }
  349.     } else {
  350.         /*--- ignore mm_exist for other. These are quick opens --*/
  351.     }
  352. }
  353.  
  354.  
  355.  
  356. /*----------------------------------------------------------------------
  357.     Receive notification from IMAP that a message has been expunged
  358.  
  359.    Args: stream -- The stream/folder the message is expunged from
  360.          number -- The message number that was expunged
  361.  
  362. mm_expunged is always called on an expunge.  Simply remove all 
  363. reference to the expunged message, shifting internal mappings as
  364. necessary.
  365.   ----*/
  366. void
  367. mm_expunged(stream, number)
  368.     MAILSTREAM *stream;
  369.     long        number;
  370. {
  371.     MSGNO_S *msgs;
  372.  
  373.     dprint(3, (debugfile, "mm_expunged called %s %ld\n",
  374.      !stream  ? "(no stream)" : !stream->mailbox ? "(null)" : stream->mailbox, 
  375.            number));
  376.  
  377.     /*
  378.      * If we ever deal with more than two streams, this'll break
  379.      */
  380.     if(stream == ps_global->mail_stream){
  381.     long i = mn_raw2m(ps_global->msgmap, number);
  382.     if(i){
  383.         while(i <= mn_get_total(ps_global->msgmap))    /* flush invalid */
  384.           clear_index_cache_ent(i++);        /* cache entries! */
  385.  
  386.         mn_flush_raw(ps_global->msgmap, number);
  387.         ps_global->mail_box_changed = 1;
  388.         ps_global->mangled_header   = 1;
  389.         ps_global->expunge_count++;
  390.     }
  391.     }
  392.     else if(stream == ps_global->inbox_stream){
  393.     long i = mn_raw2m(ps_global->msgmap, number);
  394.     if(i){
  395.         mn_flush_raw(ps_global->inbox_msgmap, number);
  396.         ps_global->inbox_changed = 1;
  397.         ps_global->inbox_expunge_count++;
  398.     }
  399.     }
  400. }
  401.  
  402.  
  403.  
  404. /*---------------------------------------------------------------------- 
  405.         receive notification that search found something           
  406.  
  407.  Input:  mail stream and message number of located item
  408.  
  409.  Result: nothing, not used by pine
  410.   ----*/
  411. void
  412. mm_searched(stream, number)
  413.     MAILSTREAM *stream;
  414.     long        number;
  415. {
  416.     if(stream == mm_search_stream)
  417.       mm_search_count++;
  418. }
  419.  
  420.  
  421.  
  422. /*----------------------------------------------------------------------
  423.       Get login and password from user for IMAP login
  424.   
  425.   Args:  host   -- The host name the user is trying to log in to 
  426.          user   -- Buffer to return the user name in 
  427.          passwd -- Buffer to return the passwd in
  428.          trial  -- The trial number or number of attempts to login
  429.  
  430.  Result: username and password passed back to imap
  431.   ----*/
  432. void
  433. mm_login(host, user, passwd, trial)
  434.     char *host;
  435.     char *user;
  436.     char *passwd;
  437.     long  trial;
  438. {
  439.     static char junk[] = {'?', '\0'};
  440.     char      prompt[80], *p, *named_user = NULL;
  441.     HelpType  help ;
  442.     int       rc, q_line;
  443.  
  444.     q_line = -FOOTER_ROWS(ps_global);            /* 3 from bottom */
  445.  
  446.     if(ps_global->anonymous) {
  447.         /*------ Anonymous login mode --------*/
  448.         if(trial >= 1) {
  449.             user[0]   = '\0';
  450.             passwd[0] = '\0';
  451.         } else {
  452.             strcpy(user, "anonymous");
  453.             sprintf(passwd, "%s@%s", ps_global->VAR_USER_ID,
  454.             ps_global->hostname);
  455.         }
  456.         return;
  457.     }
  458.  
  459.     /*
  460.      * Initialize user name with either /user= value in the stream
  461.      * being logged into (accessed via the GET_USERNAMEBUF c-client
  462.      * parameter) or the user name we're running under...
  463.      */
  464.     named_user = (char *) mail_parameters(NULL, GET_USERNAMEBUF, NULL);
  465.     if(trial == 0L)
  466.       strcpy(user, (named_user && *named_user)
  467.              ? named_user : ps_global->VAR_USER_ID);
  468.  
  469.     /*
  470.      * try last working password associated with this host.
  471.      */
  472.     if(trial == 0L && imap_get_passwd(mm_login_list, passwd, user, host))
  473.       return;
  474.  
  475. #if defined(DOS) || defined(OS2)
  476.     /* check to see if there's a password left over from last session */
  477.     if(trial == 0L && get_passfile_passwd(ps_global->pinerc,passwd,user,host)){
  478.     imap_set_passwd(&mm_login_list, passwd, user, host);
  479.     return;
  480.     }
  481. #endif
  482.  
  483.     ps_global->mangled_footer = 1;
  484.     if(!(named_user && *named_user)){
  485.     help = NO_HELP;
  486.     sprintf(prompt, "HOST: %s  ENTER LOGIN NAME: ", host);
  487.     while(1) {
  488.         rc = optionally_enter(user, q_line, 0, MAILTMPLEN - 1, 1, 0,
  489.                   prompt, NULL, help, 0);
  490.         if(rc == 3) {
  491.         help = help == NO_HELP ? h_oe_login : NO_HELP;
  492.         continue;
  493.         }
  494.         if(rc != 4)
  495.           break;
  496.     }
  497.  
  498.     if(rc == 1 || !user[0]) {
  499.         user[0]   = '\0';
  500.         passwd[0] = '\0';
  501.         return;
  502.     }
  503.     }
  504.  
  505.     help = NO_HELP;
  506.     sprintf(prompt, "HOST: %s  USER: %s  ENTER PASSWORD: ", host, user);
  507.     *passwd = '\0';
  508.     while(1) {
  509.         rc = optionally_enter(passwd, q_line, 0, MAILTMPLEN - 1, 0,
  510.                   1, prompt, NULL, help, 0);
  511.         if(rc == 3) {
  512.             help = help == NO_HELP ? h_oe_passwd : NO_HELP;
  513.             continue;
  514.         }
  515.         if(rc != 4)
  516.           break;
  517.     }
  518.  
  519.     if(rc == 1 || !passwd[0]) {
  520.     if(!named_user){
  521.         strcpy(user,junk);
  522.         strcpy(passwd, junk);
  523.     }
  524.     else{
  525.         user[0]   = '\0';
  526.         passwd[0] = '\0';
  527.     }
  528.  
  529.         return;
  530.     }
  531.  
  532.     /* remember the password for next time */
  533.     imap_set_passwd(&mm_login_list, passwd, user, host);
  534. #if defined(DOS) || defined(OS2)
  535.     /* if requested, remember it on disk for next session */
  536.     set_passfile_passwd(ps_global->pinerc, passwd, user, host);
  537. #endif
  538. }
  539.  
  540.  
  541.  
  542. /*----------------------------------------------------------------------
  543.        Receive notification of an error writing to disk
  544.       
  545.   Args: stream  -- The stream the error occured on
  546.         errcode -- The system error code (errno)
  547.         serious -- Flag indicating error is serious (mail may be lost)
  548.  
  549. Result: If error is non serious, the stream is marked as having an error
  550.         and deletes are disallowed until error clears
  551.         If error is serious this goes modal, allowing the user to retry
  552.         or get a shell escape to fix the condition. When the condition is
  553.         serious it means that mail existing in the mailbox will be lost
  554.         if Pine exits without writing, so we try to induce the user to 
  555.         fix the error, go get someone that can fix the error, or whatever
  556.         and don't provide an easy way out.
  557.   ----*/
  558. long
  559. mm_diskerror (stream, errcode, serious)
  560.     MAILSTREAM *stream;
  561.     long        errcode;
  562.     long        serious;
  563. {
  564.     int  i, j;
  565.     char *p, *q, *s;
  566.     static ESCKEY_S de_opts[] = {
  567.     {'r', 'r', "R", "Retry"},
  568.     {'f', 'f', "F", "FileBrowser"},
  569.     {'s', 's', "S", "ShellPrompt"},
  570.     {-1, 0, NULL, NULL}
  571.     };
  572. #define    DE_COLS        (ps_global->ttyo->screen_cols)
  573. #define    DE_LINE        (ps_global->ttyo->screen_rows - 3)
  574.  
  575. #define    DE_FOLDER(X)    ((X) ? (X)->mailbox : "<no folder>")
  576. #define    DE_PMT    \
  577.    "Disk error!  Choose Retry, or the File browser or Shell to clean up: "
  578. #define    DE_STR1        "SERIOUS DISK ERROR WRITING: \"%s\""
  579. #define    DE_STR2    \
  580.    "The reported error number is %s.  The last reported mail error was:"
  581.     static char *de_msg[] = {
  582.     "Please try to correct the error preventing Pine from saving your",
  583.     "mail folder.  For example if the disk is out of space try removing",
  584.     "unneeded files.  You might also contact your system administrator.",
  585.     "",
  586.     "Both Pine's File Browser and an option to enter the system's",
  587.     "command prompt are offered to aid in fixing the problem.  When",
  588.     "you believe the problem is resolved, choose the \"Retry\" option.",
  589.     "Be aware that messages may be lost or this folder left in an",
  590.     "inaccessible condition if you exit or kill Pine before the problem",
  591.     "is resolved.",
  592.     NULL};
  593.     static char *de_shell_msg[] = {
  594.     "\n\nPlease attempt to correct the error preventing saving of the",
  595.     "mail folder.  If you do not know how to correct the problem, contact",
  596.     "your system administrator.  To return to Pine, type \"exit\".",
  597.     NULL};
  598.  
  599.     dprint(0, (debugfile,
  600.        "\n***** DISK ERROR on stream %s. Error code %ld. Error is %sserious\n",
  601.            DE_FOLDER(stream), errcode, serious ? "" : "not "));
  602.     dprint(0, (debugfile, "***** message: \"%s\"\n\n", ps_global->last_error));
  603.  
  604.     if(!serious) {
  605.         if(stream == ps_global->mail_stream) {
  606.             ps_global->io_error_on_stream = 1;
  607.         }
  608.  
  609.         return (1) ;
  610.     }
  611.  
  612.     while(1){
  613.     /* replace pine's body display with screen full of explanatory text */
  614.     ClearLine(2);
  615.     PutLine1(2, max((DE_COLS - sizeof(DE_STR1)
  616.                         - strlen(DE_FOLDER(stream)))/2, 0),
  617.          DE_STR1, DE_FOLDER(stream));
  618.     ClearLine(3);
  619.     PutLine1(3, 4, DE_STR2, long2string(errcode));
  620.          
  621.     PutLine0(4, 0, "       \"");
  622.     removing_leading_white_space(ps_global->last_error);
  623.     for(i = 4, p = ps_global->last_error; *p && i < DE_LINE; ){
  624.         for(s = NULL, q = p; *q && q - p < DE_COLS - 16; q++)
  625.           if(isspace(*q))
  626.         s = q;
  627.  
  628.         if(*q && s)
  629.           q = s;
  630.  
  631.         while(p < q)
  632.           Writechar(*p++, 0);
  633.  
  634.         if(*(p = q)){
  635.         ClearLine(++i);
  636.         PutLine0(i, 0, "        ");
  637.         while(*p && isspace(*p))
  638.           p++;
  639.         }
  640.         else{
  641.         Writechar('\"', 0);
  642.         CleartoEOLN();
  643.         break;
  644.         }
  645.     }
  646.  
  647.     ClearLine(++i);
  648.     for(j = ++i; i < DE_LINE && de_msg[i-j]; i++){
  649.         ClearLine(i);
  650.         PutLine0(i, 0, "  ");
  651.         Write_to_screen(de_msg[i-j]);
  652.     }
  653.  
  654.     while(i < DE_LINE)
  655.       ClearLine(i++);
  656.  
  657.     switch(radio_buttons(DE_PMT, -FOOTER_ROWS(ps_global), de_opts,
  658.                  'r', 0, NO_HELP, RB_FLUSH_IN | RB_NO_NEWMAIL)){
  659.       case 'r' :                /* Retry! */
  660.         ps_global->mangled_screen = 1;
  661.         return(0L);
  662.  
  663.       case 'f' :                /* File Browser */
  664.         {
  665.         char full_filename[MAXPATH+1], filename[MAXPATH+1];
  666.  
  667.         filename[0] = '\0';
  668.         build_path(full_filename, ps_global->home_dir, filename);
  669.         file_lister("DISK ERROR", full_filename, filename, FALSE,
  670.                 FB_SAVE);
  671.         }
  672.  
  673.         break;
  674.  
  675.       case 's' :
  676.         EndInverse();
  677.         end_keyboard(ps_global ? F_ON(F_USE_FK,ps_global) : 0);
  678.         end_tty_driver(ps_global);
  679.         for(i = 0; de_shell_msg[i]; i++)
  680.           puts(de_shell_msg[i]);
  681.  
  682.         /*
  683.          * Don't use our piping mechanism to spawn a subshell here
  684.          * since it will the server (thus reentering c-client).
  685.          * Bad thing to do.
  686.          */
  687. #ifdef    _WINDOWS
  688. #else
  689.         system("csh");
  690. #endif
  691.         init_tty_driver(ps_global);
  692.         init_keyboard(F_ON(F_USE_FK,ps_global));
  693.         break;
  694.     }
  695.  
  696.     if(ps_global->redrawer)
  697.       (*ps_global->redrawer)();
  698.     }
  699. }
  700.  
  701.  
  702.  
  703. /*----------------------------------------------------------------------
  704.       Receive list of folders from c-client/imap.
  705.  
  706.  Puts an entry into the specified list of mail folders associated with
  707.  the global context (aka collection). 
  708.  ----*/
  709.    
  710. void
  711. context_mailbox(name)
  712.     char *name;
  713. {
  714.     dprint(4, (debugfile, "====== context_mailbox: (%s)\n", name));
  715.  
  716.     /*
  717.      * "Inbox" is filtered out here, since pine only supports one true
  718.      * inbox...
  719.      */
  720.     if((!ps_global->show_dot_names && *name == '.')
  721.        || (!find_folder_inbox && !strucmp(name,"inbox")))
  722.        return;
  723.  
  724.     find_folder_count++;
  725.     if(find_folder_list){
  726.     FOLDER_S *new_f     = new_folder(name);
  727.     new_f->prefix[0]    = '\0';
  728.     new_f->msg_count    = 0;
  729.     new_f->unread_count = 0;
  730.     folder_insert(-1, new_f, find_folder_list);
  731.     }
  732. }
  733.  
  734.  
  735.  
  736. /*----------------------------------------------------------------------
  737.      Receive list of bulliten boards from c-client/imap
  738.  
  739.  Puts an entry into the specified list of bulletin boards associated with
  740.  the global context (aka collection). 
  741.  ----*/
  742. void
  743. context_bboard(name)
  744.     char *name;
  745. {
  746.     dprint(4, (debugfile, "====== context_bboard: (%s)\n", name));
  747.  
  748. #ifdef NEWBB
  749.     /*----
  750.  
  751.        Hack to handle mm_bboard calls that result from the 
  752.        nntp_find_new_bboards call to find the newly created news groups
  753.      ----*/
  754.     if(find_folder_list == newbb_folder_list) {
  755.         mark_folder_as_new(name);
  756.         return;
  757.     }
  758. #endif
  759.  
  760.     if(!ps_global->show_dot_names && *name == '.')
  761.        return;
  762.  
  763.     find_folder_count++;
  764.     if(find_folder_list){
  765.     FOLDER_S *new_f = new_folder(name);
  766.     folder_insert(folder_total(find_folder_list), new_f, find_folder_list);
  767.     }
  768. }
  769.  
  770.  
  771. void
  772. mm_fatal(message)
  773.     char *message;
  774. {
  775.     panic(message);
  776. }
  777.  
  778.  
  779. void
  780. mm_flags(stream,number)
  781.     MAILSTREAM *stream;
  782.     long number;
  783. {
  784.     long i;
  785.  
  786.     /*
  787.      * The idea here is to clean up any data pine might have cached
  788.      * that has anything to do with the indicated message number.
  789.      * At the momment, this amounts only to cached index lines, but
  790.      * watch out for future changes...
  791.      */
  792.     if(stream != ps_global->mail_stream)
  793.       return;            /* only worry about displayed msgs */
  794.  
  795.     /* in case number is current, fix titlebar */
  796.     ps_global->mangled_header = 1;
  797.  
  798.     /* then clear index entry */
  799.     if(i = mn_raw2m(ps_global->msgmap, number))
  800.       clear_index_cache_ent(i);
  801. }
  802.  
  803.  
  804. /*
  805.  * Seconds afterwhich it's ok to prompt for connention severage...
  806.  */
  807. #define    TO_BAIL_THRESHOLD    60L
  808.  
  809.  
  810. /*
  811.  * pine_tcptimeout - C-client callback to handle tcp-related timeouts.
  812.  */
  813. long
  814. pine_tcptimeout(t)
  815.     long t;
  816. {
  817.     long rv = 1L;            /* keep trying by default */
  818.     int  ch;
  819.  
  820.     if(ps_global->noshow_timeout)
  821.       return(rv);
  822.  
  823.     /*
  824.      * Prompt after a minute (since by then things are probably really bad)
  825.      * A prompt timeout means "keep trying"...
  826.      */
  827.     if(t >= TO_BAIL_THRESHOLD){
  828.     int clear_inverse;
  829.  
  830.     ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
  831.     if(clear_inverse = !InverseState())
  832.       StartInverse();
  833.  
  834.     PutLine1(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0,
  835.        "\007Waited %s seconds for server reply.  Break connection to server? ",
  836.        long2string(t));        /* if not DOS, add fflush() */
  837.     CleartoEOLN();
  838.     fflush(stdout);
  839.     flush_input();
  840.     ch = read_char(7);
  841.     if(ch == 'y' || ch == 'Y')
  842.       rv = 0L;
  843.  
  844.     if(clear_inverse)
  845.       EndInverse();
  846.  
  847.     ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
  848.     }
  849.  
  850.     if(rv == 1L){            /* just warn 'em something's up */
  851.     q_status_message1(SM_ORDER, 0, 0,
  852.           "Waited %s seconds for server reply.  Still Waiting...",
  853.           long2string(t));
  854.     flush_status_messages(0);    /* make sure it's seen */
  855.     }
  856.  
  857.     mark_status_dirty();        /* make sure it get's cleared */
  858.     return(rv);
  859. }
  860.  
  861.  
  862.  
  863. /*----------------------------------------------------------------------
  864.    Exported method to retrieve logged in user name associated with stream
  865.  
  866.    Args: host -- host to find associated login name with.
  867.  
  868.  Result: 
  869.   ----*/
  870. char *
  871. cached_user_name(host)
  872.     char *host;
  873. {
  874.     MMLOGIN_S *l;
  875.  
  876.     if((l = mm_login_list) && host)
  877.       do
  878.     if(!strucmp(host, l->host))
  879.       return(l->user);
  880.       while(l = l->next);
  881.  
  882.     return(NULL);
  883. }
  884.  
  885.  
  886. int
  887. imap_get_passwd(l, passwd, user, host)
  888.     MMLOGIN_S *l;
  889.     char *passwd, *user, *host;
  890. {
  891.     while(l){
  892.     /* host names and, if present, user name, must match */
  893.     if(!strucmp(host, l->host) && (!*user || !strcmp(user, l->user))){
  894.         strcpy(user, l->user);
  895.         strcpy(passwd, l->passwd);
  896.         break;
  897.     }
  898.     else
  899.       l = l->next;
  900.     }
  901.  
  902.     return(l != NULL);
  903. }
  904.  
  905.  
  906.  
  907. void
  908. imap_set_passwd(l, passwd, user, host)
  909.     MMLOGIN_S **l;
  910.     char *passwd, *user, *host;
  911. {
  912.     while(*l){
  913.     if(!strucmp(host, (*l)->host)){
  914.         fs_give((void **)&(*l)->user);
  915.         fs_give((void **)&(*l)->passwd);
  916.         break;
  917.     }
  918.     else
  919.       l = &(*l)->next;
  920.     }
  921.  
  922.     if(!*l){
  923.     *l = (MMLOGIN_S *)fs_get(sizeof(MMLOGIN_S));
  924.     memset(*l, 0, sizeof(MMLOGIN_S));
  925.     }
  926.  
  927.     (*l)->user   = cpystr(user);
  928.     (*l)->passwd = cpystr(passwd);
  929.     if(!(*l)->host)
  930.       (*l)->host = cpystr(host);
  931. }
  932.  
  933.  
  934.  
  935. void
  936. imap_flush_passwd_cache()
  937. {
  938.     MMLOGIN_S *l;
  939.     while(l = mm_login_list){
  940.     mm_login_list = mm_login_list->next;
  941.     if(l->user)
  942.       fs_give((void **)&l->user);
  943.  
  944.     if(l->host)
  945.       fs_give((void **)&l->host);
  946.  
  947.     if(l->passwd)
  948.       fs_give((void **)&l->passwd);
  949.  
  950.     fs_give((void **)&l);
  951.     }
  952. }
  953.  
  954.  
  955. /* 
  956.  * DOS-specific functions to support caching username/passwd/host
  957.  * triples on disk for use from one session to the next...
  958.  */
  959. #if defined(DOS) || defined(OS2)
  960.  
  961. #define    FIRSTCH        0x20
  962. #define    LASTCH        0x7e
  963. #define    TABSZ        (LASTCH - FIRSTCH + 1)
  964.  
  965. static    int        xlate_key;
  966. static    MMLOGIN_S    *passfile_cache = NULL;
  967.  
  968.  
  969.  
  970. /*
  971.  * xlate_in() - xlate_in the given character
  972.  */
  973. char
  974. xlate_in(c)
  975.     int    c;
  976. {
  977.     register int  eti;
  978.  
  979.     eti = xlate_key;
  980.     if((c >= FIRSTCH) && (c <= LASTCH)){
  981.         eti += (c - FIRSTCH);
  982.     eti -= (eti >= 2*TABSZ) ? 2*TABSZ : (eti >= TABSZ) ? TABSZ : 0;
  983.         return((xlate_key = eti) + FIRSTCH);
  984.     }
  985.     else
  986.       return(c);
  987. }
  988.  
  989.  
  990.  
  991. /*
  992.  * xlate_out() - xlate_out the given character
  993.  */
  994. char
  995. xlate_out(c)
  996.     char c;
  997. {
  998.     register int  dti;
  999.     register int  xch;
  1000.  
  1001.     if((c >= FIRSTCH) && (c <= LASTCH)){
  1002.         xch  = c - (dti = xlate_key);
  1003.     xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0;
  1004.         dti  = (xch - FIRSTCH) + dti;
  1005.     dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0;
  1006.         xlate_key = dti;
  1007.         return(xch);
  1008.     }
  1009.     else
  1010.       return(c);
  1011. }
  1012.  
  1013.  
  1014.  
  1015. char *
  1016. passfile_name(pinerc, path)
  1017.     char *pinerc, *path;
  1018. {
  1019.     char *p = NULL;
  1020.     int   i;
  1021.     FILE *fp;
  1022. #define    PASSFILE    "pine.pwd"
  1023.  
  1024.     if(!path || !pinerc || pinerc[0] == '\0')
  1025.       return(NULL);
  1026.  
  1027.     if((p = strrchr(pinerc, '\\')) == NULL)
  1028.       p = strchr(pinerc, ':');
  1029.  
  1030.     if(p){
  1031.     strncpy(path, pinerc, i = (p - pinerc) + 1);
  1032.     path[i] = '\0';
  1033.     }
  1034.  
  1035.     strcat(path, PASSFILE);
  1036.     return(path);
  1037. }
  1038.  
  1039.  
  1040.  
  1041. int
  1042. read_passfile(pinerc, l)
  1043.     char       *pinerc;
  1044.     MMLOGIN_S **l;
  1045. {
  1046.     char  tmp[MAILTMPLEN], *ui[3];
  1047.     int   i, j, n;
  1048.     FILE *fp;
  1049.  
  1050.     /* if there's no password to read, bag it!! */
  1051.     if(!passfile_name(pinerc, tmp) || !(fp = fopen(tmp, "r")))
  1052.       return(0);
  1053.  
  1054.     for(n = 0; fgets(tmp, MAILTMPLEN, fp); n++){
  1055.     /*** do any necessary DEcryption here ***/
  1056.     xlate_key = n;
  1057.     for(i = 0; tmp[i]; i++)
  1058.       tmp[i] = xlate_out(tmp[i]);
  1059.  
  1060.     if(i && tmp[i-1] == '\n')
  1061.       tmp[i-1] = '\0';            /* blast '\n' */
  1062.  
  1063.     ui[0] = ui[1] = ui[2] = NULL;
  1064.     for(i = 0, j = 0; tmp[i] && j < 3; j++){
  1065.         for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
  1066.           ;                    /* find end of data */
  1067.  
  1068.         if(tmp[i])
  1069.           tmp[i++] = '\0';            /* tie off data */
  1070.     }
  1071.  
  1072.     if(ui[0] && ui[1] && ui[2])        /* valid field? */
  1073.       imap_set_passwd(l, ui[0], ui[1], ui[2]);
  1074.     }
  1075.  
  1076.     fclose(fp);
  1077.     return(1);
  1078. }
  1079.  
  1080.  
  1081.  
  1082. void
  1083. write_passfile(pinerc, l)
  1084.     char      *pinerc;
  1085.     MMLOGIN_S *l;
  1086. {
  1087.     char  tmp[MAILTMPLEN];
  1088.     int   i, n;
  1089.     FILE *fp;
  1090.  
  1091.     /* if there's no password to read, bag it!! */
  1092.     if(!passfile_name(pinerc, tmp) || !(fp = fopen(tmp, "w")))
  1093.       return;
  1094.  
  1095.     for(n = 0; l; l = l->next, n++){
  1096.     /*** do any necessary ENcryption here ***/
  1097.     sprintf(tmp, "%s\t%s\t%s\n", l->passwd, l->user, l->host);
  1098.     xlate_key = n;
  1099.     for(i = 0; tmp[i]; i++)
  1100.       tmp[i] = xlate_in(tmp[i]);
  1101.  
  1102.     fputs(tmp, fp);
  1103.     }
  1104.  
  1105.     fclose(fp);
  1106. }
  1107.  
  1108.  
  1109.  
  1110. /*
  1111.  * get_passfile_passwd - return the password contained in the special passord
  1112.  *            cache.  The file is assumed to be in the same directory
  1113.  *            as the pinerc with the name defined above.
  1114.  */
  1115. int
  1116. get_passfile_passwd(pinerc, passwd, user, host)
  1117.     char *pinerc, *passwd, *user, *host;
  1118. {
  1119.     return((passfile_cache || read_passfile(pinerc, &passfile_cache))
  1120.          ? imap_get_passwd(passfile_cache, passwd, user, host) : 0);
  1121. }
  1122.  
  1123.  
  1124.  
  1125. /*
  1126.  * set_passfile_passwd - set the password file entry associated with
  1127.  *            cache.  The file is assumed to be in the same directory
  1128.  *            as the pinerc with the name defined above.
  1129.  */
  1130. void
  1131. set_passfile_passwd(pinerc, passwd, user, host)
  1132.     char *pinerc, *passwd, *user, *host;
  1133. {
  1134.     if((passfile_cache || read_passfile(pinerc, &passfile_cache))
  1135.        && want_to("Preserve password on DISK for next login", 'y', 'x',
  1136.           NO_HELP, 0, 0) == 'y'){
  1137.     imap_set_passwd(&passfile_cache, passwd, user, host);
  1138.     write_passfile(pinerc, passfile_cache);
  1139.     }
  1140. }
  1141. #endif    /* DOS */
  1142.